package drawing;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.CubicCurve2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// 主类，继承自 JFrame 用于创建窗口
public class DrawingSoftware extends JFrame {
    private DrawingPanel drawingPanel;
    private JButton clearButton;
    private JButton newCurveButton;
    private JButton closeCurveButton;

    public DrawingSoftware() {
        // 设置窗口标题
        setTitle("画图软件");
        // 设置窗口关闭时的操作
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 设置窗口布局为边界布局
        setLayout(new BorderLayout());

        // 创建绘图面板
        drawingPanel = new DrawingPanel();
        // 将绘图面板添加到窗口中心
        add(drawingPanel, BorderLayout.CENTER);

        // 创建清除按钮
        clearButton = new JButton("重新绘制");
        // 为清除按钮添加点击事件监听器
        clearButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 点击按钮时，清空绘图面板上的点和曲线
                drawingPanel.clear();
            }
        });

        // 创建开始新曲线的按钮
        newCurveButton = new JButton("开始新曲线");
        newCurveButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                drawingPanel.startNewCurve();
            }
        });

        // 创建闭合曲线的按钮
        closeCurveButton = new JButton("闭合当前曲线");
        closeCurveButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                drawingPanel.closeCurrentCurve();
            }
        });

        // 创建一个面板用于放置按钮
        JPanel buttonPanel = new JPanel();
        // 将清除按钮、开始新曲线的按钮和闭合曲线的按钮添加到按钮面板
        buttonPanel.add(clearButton);
        buttonPanel.add(newCurveButton);
        buttonPanel.add(closeCurveButton);
        // 将按钮面板添加到窗口底部
        add(buttonPanel, BorderLayout.SOUTH);

        // 自动调整窗口大小以适应组件
        pack();
        // 显示窗口
        setVisible(true);
    }

    public static void main(String[] args) {
        // 在事件调度线程中创建并显示 GUI
        SwingUtilities.invokeLater(DrawingSoftware::new);
    }
}

// 绘图面板类，继承自 JPanel
class DrawingPanel extends JPanel {
    private List<List<Point>> curvePoints; // 存储每段曲线的点
    private List<List<CubicCurve2D>> allCurves; // 存储每段曲线的所有曲线
    private List<Boolean> closedCurves; // 存储每段曲线是否闭合
    private CubicCurve2D selectedCurve;
    private Point dragStart;
    private Point selectedPoint;
    // 用于存储曲线的控制点信息，键为曲线ID，值为控制点映射
    private Map<Integer, Map<String, CubicCurve2D>> curveControlPoints;
    private boolean startNewCurve = false;
    private int nextCurveId = 0; // 下一个可用的曲线ID
    private List<Integer> curveIds; // 每段曲线的ID

    public DrawingPanel() {
        // 初始化每段曲线的点列表
        curvePoints = new ArrayList<>();
        // 初始化每段曲线的曲线列表
        allCurves = new ArrayList<>();
        // 初始化曲线闭合状态列表
        closedCurves = new ArrayList<>();
        // 初始化选中的曲线为 null
        selectedCurve = null;
        // 初始化拖动起始点为 null
        dragStart = null;
        // 初始化选中的点为 null
        selectedPoint = null;
        // 初始化曲线控制点映射
        curveControlPoints = new HashMap<>();
        // 初始化曲线ID列表
        curveIds = new ArrayList<>();

        // 设置面板的首选大小
        setPreferredSize(new Dimension(800, 600));

        // 添加鼠标监听器
        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                // 鼠标按下时的处理
                Point clickPoint = e.getPoint();
                // 检查是否点击了点
                selectedPoint = getSelectedPoint(clickPoint);
                if (selectedPoint != null) {
                    // 如果点击了点，记录拖动起始点
                    dragStart = clickPoint;
                } else {
                    // 检查是否点击了曲线
                    selectedCurve = getSelectedCurve(clickPoint);
                    if (selectedCurve != null) {
                        // 如果点击了曲线，记录拖动起始点
                        dragStart = clickPoint;
                    } else {
                        if (startNewCurve || curvePoints.isEmpty()) {
                            // 开始新的曲线绘制
                            List<Point> newPoints = new ArrayList<>();
                            newPoints.add(clickPoint);
                            curvePoints.add(newPoints);
                            closedCurves.add(false); // 新曲线默认不闭合
                            curveIds.add(nextCurveId++); // 分配新的曲线ID
                            curveControlPoints.put(curveIds.get(curveIds.size() - 1), new HashMap<>());
                            startNewCurve = false;
                        } else {
                            // 继续当前曲线的绘制
                            List<Point> currentPoints = curvePoints.get(curvePoints.size() - 1);
                            // 检查当前曲线是否已闭合
                            if (!closedCurves.get(closedCurves.size() - 1)) {
                                currentPoints.add(clickPoint);
                            } else {
                                // 如果已闭合，创建新曲线
                                List<Point> newPoints = new ArrayList<>();
                                newPoints.add(clickPoint);
                                curvePoints.add(newPoints);
                                closedCurves.add(false);
                                curveIds.add(nextCurveId++);
                                curveControlPoints.put(curveIds.get(curveIds.size() - 1), new HashMap<>());
                            }
                        }
                        // 重新计算曲线
                        calculateCurves();
                    }
                }
                // 重绘面板
                repaint();
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                // 鼠标释放时，重置选中的曲线、拖动起始点和选中的点
                selectedCurve = null;
                dragStart = null;
                selectedPoint = null;
            }

            @Override
            public void mouseClicked(MouseEvent e) {
                // 鼠标点击时的处理
                if (e.getClickCount() == 2) {
                    // 双击时检查是否点击了点
                    Point clickPoint = e.getPoint();
                    Point pointToRemove = getSelectedPoint(clickPoint);
                    if (pointToRemove != null) {
                        // 如果点击了点，删除该点
                        for (int i = 0; i < curvePoints.size(); i++) {
                            List<Point> points = curvePoints.get(i);
                            if (points.remove(pointToRemove)) {
                                // 如果删除点后曲线点数量不足，标记为不闭合
                                if (points.size() < 3) {
                                    closedCurves.set(i, false);
                                }
                                // 清除该曲线段的所有控制点信息，因为点的索引可能已经改变
                                int curveId = curveIds.get(i);
                                curveControlPoints.put(curveId, new HashMap<>());
                                break;
                            }
                        }
                        // 重新计算曲线
                        calculateCurves();
                        // 重绘面板
                        repaint();
                    }
                }
            }
        });

        // 添加鼠标移动监听器
        addMouseMotionListener(new MouseAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                // 鼠标拖动时的处理
                if (selectedPoint != null && dragStart != null) {
                    // 如果选中了点，移动该点
                    Point currentPoint = e.getPoint();
                    int dx = currentPoint.x - dragStart.x;
                    int dy = currentPoint.y - dragStart.y;
                    selectedPoint.x += dx;
                    selectedPoint.y += dy;
                    // 更新拖动起始点
                    dragStart = currentPoint;
                    // 清除该曲线段的所有控制点信息，因为点的位置已经改变
                    for (int i = 0; i < curvePoints.size(); i++) {
                        List<Point> points = curvePoints.get(i);
                        if (points.contains(selectedPoint)) {
                            int curveId = curveIds.get(i);
                            curveControlPoints.put(curveId, new HashMap<>());
                            break;
                        }
                    }
                    // 重新计算曲线
                    calculateCurves();
                    // 重绘面板
                    repaint();
                } else if (selectedCurve != null && dragStart != null) {
                    // 如果选中了曲线，移动曲线的控制点
                    Point currentPoint = e.getPoint();
                    int dx = currentPoint.x - dragStart.x;
                    int dy = currentPoint.y - dragStart.y;
                    selectedCurve.setCurve(selectedCurve.getX1(), selectedCurve.getY1(),
                            selectedCurve.getCtrlX1() + dx, selectedCurve.getCtrlY1() + dy,
                            selectedCurve.getCtrlX2() + dx, selectedCurve.getCtrlY2() + dy,
                            selectedCurve.getX2(), selectedCurve.getY2());
                    // 更新拖动起始点
                    dragStart = currentPoint;
                    // 保存曲线的控制点信息
                    saveCurveControlPoints(selectedCurve);
                    // 重绘面板
                    repaint();
                }
            }
        });
    }

    // 开始新的曲线绘制
    public void startNewCurve() {
        startNewCurve = true;
    }

    // 闭合当前曲线
    public void closeCurrentCurve() {
        if (curvePoints.isEmpty()) {
            return;
        }
        int currentCurveIndex = curvePoints.size() - 1;
        List<Point> currentPoints = curvePoints.get(currentCurveIndex);
        // 至少需要3个点才能形成闭合曲线
        if (currentPoints.size() >= 3 && !closedCurves.get(currentCurveIndex)) {
            closedCurves.set(currentCurveIndex, true);
            // 清除该曲线段的所有控制点信息，因为曲线状态已改变
            int curveId = curveIds.get(currentCurveIndex);
            curveControlPoints.put(curveId, new HashMap<>());
            calculateCurves();
            repaint();
        }
    }

    // 清空面板上的点和曲线的方法
    public void clear() {
        curvePoints.clear();
        allCurves.clear();
        closedCurves.clear();
        curveControlPoints.clear();
        curveIds.clear();
        nextCurveId = 0;
        repaint();
    }

    // 计算曲线的方法，使用三次样条插值思想来平滑拟合
    private void calculateCurves() {
        // 清空所有曲线列表
        allCurves.clear();
        for (int curveIndex = 0; curveIndex < curvePoints.size(); curveIndex++) {
            List<Point> points = curvePoints.get(curveIndex);
            List<CubicCurve2D> curves = new ArrayList<>();
            int n = points.size();
            if (n < 2) {
                continue;
            }
            boolean isClosed = closedCurves.get(curveIndex);
            int loopEnd = isClosed ? n : n - 1;
            int curveId = curveIds.get(curveIndex);
            Map<String, CubicCurve2D> curveIdControlPoints = curveControlPoints.get(curveId);

            for (int i = 0; i < loopEnd; i++) {
                Point p1 = points.get(i);
                // 对于闭合曲线，最后一个点连接到第一个点
                Point p2 = points.get((i + 1) % n);

                // 计算前一个点和下一个点，用于平滑处理
                Point p0 = points.get((i - 1 + n) % n);
                Point p3 = points.get((i + 2) % n);

                // 计算更平滑的控制点
                double dx1 = p2.x - p0.x;
                double dy1 = p2.y - p0.y;
                double ctrlX1 = p1.x + dx1 / 4.0;
                double ctrlY1 = p1.y + dy1 / 4.0;

                double dx2 = p1.x - p3.x;
                double dy2 = p1.y - p3.y;
                double ctrlX2 = p2.x + dx2 / 4.0;
                double ctrlY2 = p2.y + dy2 / 4.0;

                // 创建三次贝塞尔曲线
                CubicCurve2D curve = new CubicCurve2D.Double(p1.x, p1.y, ctrlX1, ctrlY1, ctrlX2, ctrlY2, p2.x, p2.y);

                // 查找之前可能已调整过的曲线的控制点信息
                String curveKey = getCurveSegmentKey(p1, p2, i);
                CubicCurve2D prevCurve = curveIdControlPoints.get(curveKey);
                if (prevCurve != null) {
                    // 如果找到之前的曲线，保持其控制点不变
                    curve.setCurve(prevCurve.getX1(), prevCurve.getY1(),
                            prevCurve.getCtrlX1(), prevCurve.getCtrlY1(),
                            prevCurve.getCtrlX2(), prevCurve.getCtrlY2(),
                            prevCurve.getX2(), prevCurve.getY2());
                }
                // 将曲线添加到当前段的曲线列表
                curves.add(curve);
            }
            // 将当前段的曲线列表添加到所有曲线列表
            allCurves.add(curves);
        }
    }

    // 生成曲线段的唯一键
    private String getCurveSegmentKey(Point p1, Point p2, int segmentIndex) {
        return segmentIndex + "-" + p1.x + "," + p1.y + "-" + p2.x + "," + p2.y;
    }

    // 保存曲线的控制点信息
    private void saveCurveControlPoints(CubicCurve2D curve) {
        // 查找该曲线所属的曲线段和索引
        for (int curveIndex = 0; curveIndex < allCurves.size(); curveIndex++) {
            List<CubicCurve2D> curves = allCurves.get(curveIndex);
            for (int i = 0; i < curves.size(); i++) {
                if (curves.get(i) == curve) {
                    List<Point> points = curvePoints.get(curveIndex);
                    Point p1 = points.get(i);
                    Point p2 = points.get((i + 1) % points.size());

                    int curveId = curveIds.get(curveIndex);
                    String curveKey = getCurveSegmentKey(p1, p2, i);

                    curveControlPoints.get(curveId).put(curveKey, curve);
                    return;
                }
            }
        }
    }

    // 获取选中的点的方法
    private Point getSelectedPoint(Point point) {
        for (List<Point> points : curvePoints) {
            for (Point p : points) {
                if (Math.abs(p.x - point.x) <= 5 && Math.abs(p.y - point.y) <= 5) {
                    return p;
                }
            }
        }
        return null;
    }

    // 获取选中的曲线的方法
    private CubicCurve2D getSelectedCurve(Point point) {
        for (List<CubicCurve2D> curves : allCurves) {
            for (CubicCurve2D curve : curves) {
                if (curve.intersects(point.x - 5, point.y - 5, 10, 10)) {
                    return curve;
                }
            }
        }
        return null;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        // 设置抗锯齿，使绘制更平滑
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // 绘制曲线
        g2d.setColor(Color.BLUE);
        for (List<CubicCurve2D> curves : allCurves) {
            for (CubicCurve2D curve : curves) {
                g2d.draw(curve);
            }
        }

        // 绘制点，使用红色实心圆清晰标示
        g2d.setColor(Color.RED);
        for (List<Point> points : curvePoints) {
            for (Point point : points) {
                g2d.fillOval(point.x - 5, point.y - 5, 10, 10);
            }
        }
    }
}